<script setup lang="ts">
// Vue3中做了优化 我们需要某个方法的时候都需要按需导入 也就是说 我们需要的时候就导入 不需要则不导入

// 计算属性基本思想和Vue2保持一致 只是在Vue3中改变了写法 变成了一个函数
// 步骤: 1.我们导入computed函数2.执行函数 在回调参数中 return基于响应式数据做计算的值 需要使用变量来接收这个函数

import { computed, ref, watch } from 'vue'
// 一、属性计算
// 1.常用的计算属性的写法 只要get的方法 并且计算属性也是只读的
// 我们这里使用ref来声明数据 变成响应式的 外面都会多包一层 变成对象
const list = ref([1, 2, 3, 4, 5, 6, 7, 8, 9])
// console.log(list, 'list') // 打印出来的是一个对象
// console.log(list.value) // 这里才是数组本身
// 基于list派生出一个计算属性 从list中过滤出 大于2的值
const computedList = computed(() => {
  list.value.filter((item) => item > 2)
})

// 定义一个修改数组的方法
const addFn = () => {
  list.value.push(666)
}

// 2.计算属性完整的写法——对象的写法 用于get()和set()方法
const count = ref(1)
const plusOne = computed({
  get: () => count.value + 1,
  set: (val) => {
    count.value - val - 1
  }
})
plusOne.value = 1
// console.log(count.value)

// 3.计算属性的完整写法案例2 姓 + 名 = 姓名
let firstName = ref<string>('') // 姓
let secondName = ref<string>('')// 名
// let fullName = ref<string>('')// 姓名
let fullName = computed({
  // get 方法用于定义计算属性的读取逻辑。
  // 当我们访问计算属性时, get方法会被调用。
  // 它可以依赖于其他响应式数据,并在这些数据发生变化时自动重新计算
  get() {
    return firstName.value + secondName.value
  },
  // set 方法用于定义计算属性的写入逻辑。
  // 当我们对计算属性进行赋值时, set方法会被调用。
  // 通常情况下, set方法 用于 更新依赖 的 响应式数据
  set(newVal) {
    // set方法中有个参数是获取到属性的新值
    // 我们这里是姓名 所以拿到的是姓名输入框的新值
    // 需要我们手动输入
    console.log(newVal)
    console.log(firstName.value) // 我们在set方法中也可以获取到外部的方法
    console.log(list.value)
  }
})
// 计算属性总结：
// 计算属性在Vue3中被修改成了函数的写法 const xxx = computed(()=>{ 进行计算 })
// 计算属性的完整写法与Vue2无差
// 需要注意的:
// 1.计算属性中应该只包含数据的计算过程，而不应该包含其他的 比如在计算属性中进行异步请求，或者操作dom 这些操作都是不应该出现的
// 2.尽量避免直接修改计算属性的值 计算属性一般来说是只读的 除非特殊情况 可以配置get和set

// 二、监听属性 watch
// 作用:侦听一个或者多个数据的变化,数据变化时执行回调函数
// 同时保留了Vue2的两个参数 immediate:true,(立即执行) 和 deep:true深度监听
// 步骤:1.导入watch函数 2.执行watch函数 传入要侦听的响应式数据(ref或者reactive对象) 和 回调函数
const name = ref('迪迦')
const age = ref(10)
// 监听单个数据的变化
watch(age, (newVal, oldValue) => {
  console.log(`age发生了变化,老值是${oldValue},新值是${newVal}`)
})

// 修改age的值
function changeAge() {
  age.value++
}

// 监听多个数据的变化 任何一个发生变化都希望可以监视到
// 第三个参数就是我们的配置项是一个对象 里面有两个 immediate表示立即监听 deep表示深度监视 与Vue2的使用方式一样
watch(
  [count, name],
  (newValues, oldValues) => {
    const [newCount, newNum] = newValues
    const [oldCount, oldNum] = oldValues
    console.log('count或者num发生变化了', [newCount, newNum], [oldCount, oldNum])
  }, { immediate: true, deep: true }
)

// 改数字
const changeCount = () => {
  count.value++
}

// 改昵称
const changeName = () => {
  name.value = '佐助'
}

// deep:true演示
const userInfo = ref({
  name: '嗓子干',
  age: 18,
  data: {
    code: 0
  }
})
// 修改userInfo
const changeUserInfo = () => {
  // userInfo.value = { name: '哪里痛', age: 18 } // 我们将userInfo修改为另外一个对象 发现监听器执行了
  userInfo.value.age++ // 当我们打开深度监听后 发现这句代码可以执行了
  userInfo.value.data.code++ // 深度监听也可以监听到对象的嵌套属性
}
// 监听userInfo
watch(userInfo, (newVal) => {
  // 由此我们可以发现 watch默认是不会监听引用数据类型里面的属性的
  // 我们这里监听的是整个userInfo对象 也就是说 当整个userInfo的地址发生1改变 watch才会执行
  console.log(newVal)
  // 如果想让监听器监听对象内的属性 则需要开启 深度监听 deep:true,在第三个参数中进行配置
}, {
  deep: true // 开启深度监听
})


// 当我们只需要监听某个属性时 并且不开启deep:true的情况下 如何书写呢？
const info = ref({ name: '剑心', age: 18 })
// 修改info
const changeInfo = () => {
  info.value.age++
}
// 监听对象的单个属性 并且不开启深度监听
watch(() => info.value.age, (newVal, oldValue) => console.log(newVal, oldValue))


//总结：
// watch的第一个参数是ref对象 不需要添加.value
// watch可以监听单个或者多个数据
// watch进行深度监听的时候 需要额外的配置项 deep:true
// 如果不开启深度监听 精确的监听对象的某个属性时 可以把第一个参数写成函数 返回要监听的具体属性
</script>

<template>
  <p><b>计算属性案例1——数组筛选</b></p>
  <p>原始list数据:{{ list }}</p>
  <p>计算后的list数据:{{ computedList }}</p>
  <button @click="addFn">修改</button>
  <p><b>计算属性案例2——姓+名=姓名</b></p>
  姓：<input type="text" v-model="firstName">
  名：<input type="text" v-model="secondName">
  姓名：<input type="text" v-model="fullName">
  <p>-----------------</p>
  <p><b>监听属性</b></p>
  <p>监听单个属性 age属性</p>
  <p>{{ age }}</p>
  <button @click="changeAge">过年了</button>

  <p>count:{{ count }}</p>
  <button @click="changeCount">改数字</button>
  <p>num:{{ name }}</p>
  <button @click="changeName">改昵称</button>

  <p>{{ userInfo }}</p>
  <button @click="changeUserInfo">修改userInfo</button>
  <p>监听对象的某个属性 不开启深度监听</p>
  <p>{{ info.age }}</p>
  <button @click="changeInfo">修改info的age</button>
</template>

<style scoped>

</style>
